<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

/**
 * Example:
 * 
 * $this->load->library('cache');
 * $this->cache->setDriver(Cache::CACHE_DRIVER_MEMCACHED);
 * $server = new Memcached();
 * $server->addServer('localhost',11211);
 * $server->set('test',1,20);
 * $server->quit();
 * echo $server->get('test');
 */

class Cache {

	const CACHE_DRIVER_DUMMY = 'dummy';
	const CACHE_DRIVER_FILE = 'file';
	const CACHE_DRIVER_MEMCACHED = 'memcached';

	const MEMCACHED_DEFAULT_PORT = 11211;
	const MEMCACHED_HASH_BASE = 4294967296;

	protected $_supported_driver = array(
		Cache::CACHE_DRIVER_DUMMY,
		Cache::CACHE_DRIVER_FILE,
		Cache::CACHE_DRIVER_MEMCACHED,
	);
	protected $_memcached_server_list = array(
		array('address'=>'localhost','position'=>0),
		array('address'=>'10.0.0.103','position'=>0.25),
		array('address'=>'127.0.0.1','position'=>0.5),
	);
	protected $_cache_driver = false;
	protected $_cache_settings = array();

/*
| -------------------------------------------------------------------
|  PUBLIC Functions
| -------------------------------------------------------------------
*/
	public function __construct(){
		$this->_cache_driver = Cache::CACHE_DRIVER_DUMMY;
		$this->_cache_settings = array();
	}

	public function setDriver($driver){
		if(in_array($driver,$this->_supported_driver)){
			$this->_cache_driver = $driver;
		}else{
			$this->_cache_driver = Cache::CACHE_DRIVER_DUMMY;
		}

		if($this->_cache_driver == Cache::CACHE_DRIVER_MEMCACHED && !extension_loaded('memcached')){
			$this->_cache_driver = Cache::CACHE_DRIVER_DUMMY;
		}

		$function = '_'.$this->_cache_driver.'Init';
		if(method_exists($this,$function)) return $this->{$function}();
		return false;
	}

	public function get($key){
		$function = '_'.$this->_cache_driver.'Get';
		if(method_exists($this,$function)) return $this->{$function}($key);
		return false;
	}

	public function set($key,$value,$time = 60){
		$function = '_'.$this->_cache_driver.'Set';
		if(method_exists($this,$function)) return $this->{$function}($key,$value,$time);
		return false;
	}

	public function delete($key){
		$function = '_'.$this->_cache_driver.'Delete';
		if(method_exists($this,$function)) return $this->{$function}($key);
		return false;
	}

	public function close(){
		$function = '_'.$this->_cache_driver.'Close';
		if(method_exists($this,$function)) return $this->{$function}();
		return true;
	}
/*
| -------------------------------------------------------------------
|  DUMMY Functions
| -------------------------------------------------------------------
*/
	protected function _dummyInit(){return true;}
	protected function _dummyGet($key){return false;}
	protected function _dummySet($key,$value,$time){return true;}
	protected function _dummyDelete($key){return true;}
	protected function _dummyClose(){return true;}
/*
| -------------------------------------------------------------------
|  FILE Functions
| -------------------------------------------------------------------
*/
	protected function _fileInit(){
		$this->_cache_settings = array(
			'cache_path' => APPPATH.'cache/',
		);

		return true;
	}

	protected function _fileGet($key){
		if(!isset($this->_cache_settings['cache_path'])) return false;
		if(!file_exists($this->_cache_settings['cache_path'].$key)) return false;

		$data = file_get_contents($this->_cache_settings['cache_path'].$key);
		$data = unserialize($data);

		if(time() > $data['time'] + $data['ttl']){
			unlink($this->_cache_settings['cache_path'].$key);
			return false;
		}

		return $data['data'];
	}

	protected function _fileSet($key,$value,$time){
		if(!isset($this->_cache_settings['cache_path'])) return false;

		$data = array('data' => $value,'time' => time(),'ttl' => $time);
		if(file_put_contents($this->_cache_settings['cache_path'].$key,serialize($data))){
			@chmod($this->_cache_settings['cache_path'].$key,0777);
			return true;
		}

		return false;
	}

	protected function _fileDelete($key){
		if(!isset($this->_cache_settings['cache_path'])) return true;
		if(!file_exists($this->_cache_settings['cache_path'].$key)) return true;

		unlink($this->_cache_settings['cache_path'].$key);
		return true;
	}

	protected function _fileClose(){return true;}
/*
| -------------------------------------------------------------------
|  MEMCACHED Functions
| -------------------------------------------------------------------
*/
	protected function _memcachedInit(){
		$this->_cache_settings = array(
			'server' => new Memcached(),
			'current_server' => false,
		);
		
		return true;
	}

	protected function _memcachedGet($key){
		if(!isset($this->_cache_settings['server'])) return false;

		$server = $this->_crc32Server($key);
		if($this->_cache_settings['current_server'] != $server){
			$this->_cache_settings['server']->quit();
			$this->_cache_settings['server']->addServer($server,Cache::MEMCACHED_DEFAULT_PORT);
			$this->_cache_settings['current_server'] = $server;
		}

		return $this->_cache_settings['server']->get($key);
	}

	protected function _memcachedSet($key,$value,$time){
		if(!isset($this->_cache_settings['server'])) return true;

		$server = $this->_crc32Server($key);
		if($this->_cache_settings['current_server'] != $server){
			$this->_cache_settings['server']->quit();
			$this->_cache_settings['server']->addServer($server,Cache::MEMCACHED_DEFAULT_PORT);
			$this->_cache_settings['current_server'] = $server;
		}

		$this->_cache_settings['server']->set($key,$value,$time);
		return true;
	}

	protected function _memcachedDelete($key){
		if(!isset($this->_cache_settings['server'])) return true;

		$server = $this->_crc32Server($key);
		if($this->_cache_settings['current_server'] != $server){
			$this->_cache_settings['server']->quit();
			$this->_cache_settings['server']->addServer($server,Cache::MEMCACHED_DEFAULT_PORT);
			$this->_cache_settings['current_server'] = $server;
		}

		return $this->_cache_settings['server']->delete($key);
	}

	protected function _memcachedClose(){
		if(!isset($this->_cache_settings['server'])) return true;
		$this->_cache_settings['server']->quit();
		return true;
	}

	protected function _crc32Server($str){
		$str = crc32($str);
		if(PHP_INT_SIZE == 4) $str = sprintf("%u",$str);
		$res = round($str / Cache::MEMCACHED_HASH_BASE,3);

		reset($this->_memcached_server_list);
		$default = current($this->_memcached_server_list);
		foreach($this->_memcached_server_list as $server){
			if($res < $server['position']) return $server['address'];
		}

		return $default['address'];
	}

	public function debug(){
		$keys = $this->_cache_settings['server']->getAllKeys();
		$res = array();
		foreach($keys as $key){
			$res[$key] = $this->_cache_settings['server']->get($key);
		}
		return $res;
	}
}

/* End of file cache.php */
/* Location: ./application/libraries/cache.php */